
<!DOCTYPE html>
<html lang="">


<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
  <meta name="theme-color" content="#202020"/>
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  <script src="/js/busuanzi.pure.mini.js" async></script>
  
  
    <meta name="keywords" content="Emberjs,Ember-Demo," />
  

  
    <meta name="description" content="一个专注一coding的网站。提供丰富编程知识，包括Spring、Java、EmberJS、SpringBoot等等技术。" />
  
  
  <link rel="icon" type="image/x-icon" href="/image/favicon.ico">
  <title>模型，保存数据到数据库 [ Keep Coding ]</title>
  
    <!-- stylesheets list from config.yml -->
    
      <link rel="stylesheet" href="/css/pure-min.css">
    
      <link rel="stylesheet" href="/css/xoxo.css">
    
  
<meta name="generator" content="Hexo 5.0.2"><link rel="alternate" href="/atom.xml" title="Keep Coding" type="application/atom+xml">
</head>

<body>
  <div class="nav-container">
    <nav class="home-menu pure-menu pure-menu-horizontal">
  <a class="pure-menu-heading" href="/">
    <img class="avatar" src="/image/favicon.ico">
    <span class="title">Keep Coding</span>
  </a>

  <ul class="pure-menu-list clearfix">
      
          
            <li class="pure-menu-item"><a href="/" class="pure-menu-link">首页</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/archives" class="pure-menu-link">归档</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/tags" class="pure-menu-link">标签</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/search" class="pure-menu-link">搜索</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/about" class="pure-menu-link">关于</a></li>
          
      
          
            <li class="pure-menu-item"><a href="/atom.xml" class="pure-menu-link">订阅</a></li>
          
      
  </ul>
   
</nav>
  </div>

  <div class="container" id="content-outer">
    <div class="inner" id="content-inner">
      <div class="post-container">
  <article class="post" id="post">
    <header class="post-header text-center">
      <h1 class="title">
        模型，保存数据到数据库
      </h1>
      <span>
        
        <time class="time" datetime="2020-01-18T14:50:36.000Z">
        2020-01-18
      </time>
        
      </span>
      <span class="slash">/</span>
      <span class="post-meta">
      <span class="post-tags">
        <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Ember-Demo/" rel="tag">Ember-Demo</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Emberjs/" rel="tag">Emberjs</a></li></ul>
      </span>
    </span>
      <span class="slash">/</span>
      <span class="read">
      <span id="busuanzi_value_page_pv"></span> 点击
    </span>
      <span class="slash">/</span>
      <span class="read">阅读耗时 13 分钟</span>
    </header>

    <div class="post-content">
      <p>来源：<a target="_blank" rel="noopener" href="http://yoember.com/">yoember.com</a><br>作者：<a target="_blank" rel="noopener" href="http://zoltan.nz/">Zoltan</a></p>
<p><strong>声明</strong>：<em>本文的转载与翻译是经过作者认可的，再次感谢原作，如有侵权请给我留言，我会删除博文！！</em> 希望本系列教程能帮助更多学习Ember.js的初学者。</p>
<p>接着前面的两篇：</p>
<ol>
<li><a href="http://xcoding.tech/2016/03/30/Ember-Demo/%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA%E4%BB%A5%E5%8F%8A%E4%BD%BF%E7%94%A8Ember.js%E5%88%9B%E5%BB%BA%E7%AC%AC%E4%B8%80%E4%B8%AA%E9%9D%99%E6%80%81%E9%A1%B5%E9%9D%A2/">环境搭建以及使用Ember.js创建第一个静态页面</a></li>
<li><a href="http://xcoding.tech/2016/03/31/Ember-Demo/%E5%BC%95%E5%85%A5%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E3%80%81action%E3%80%81%E5%8A%A8%E6%80%81%E5%86%85%E5%AE%B9/">引入计算属性、action、动态内容</a></li>
</ol>
<p>继续为读者介绍如何使用<a target="_blank" rel="noopener" href="http://emberjs.com/">Ember</a>构建一个完整的、复杂的项目。</p>
<h2 id="第一个Ember-js模型"><a href="#第一个Ember-js模型" class="headerlink" title="第一个Ember.js模型"></a>第一个Ember.js模型</h2><p>在前面两篇中实现了如何获取界面输入的邮箱值，但是并没有真正保存到数据，仅仅只是获取界面输入的值并显示出来。在本篇中将为读者演示如何保存数据到数据库中。但是我并不会去创建一个数据库，而是使用<a target="_blank" rel="noopener" href="http://firebase.com/">firebase</a>，更多有关firebase的信息请自行查阅资料学习（如果访问firebase官网很慢或者是无法访问那么你需要fanqiang）！</p>
<p>言归正传，回到Ember的模型介绍中来。简单讲Ember的模型其实就是一个与数据表对应的一个实体类，与<code>Java</code>中的<code>JavaBean</code>有点类似。<br>创建一个模型也非常简单，可以直接使用<a target="_blank" rel="noopener" href="http://ember-cli.com/user-guide">Ember CLI</a>命令创建，下面的命令就是用于创建模型类，并在模型中增加一个<code>string</code>类型的属性<code>email</code>。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ember g model invitation email:string</span><br></pre></td></tr></table></figure>
<p>命令执行完毕之后可以在项目对应目录下看到创建的文件<code>app/models/invitaction.js</code>，文件内如如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/models/invitation.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> DS <span class="keyword">from</span> <span class="string">&#x27;ember-data&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> DS.Model.extend(&#123;</span><br><span class="line">  email: DS.attr(<span class="string">&#x27;string&#x27;</span>)</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>有了模型类之后修改控制器<code>index.js</code>的代码，加入模型，通过模型来保存数据对象。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/controller/index.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Ember <span class="keyword">from</span> <span class="string">&#x27;ember&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Ember.Controller.extend(&#123;</span><br><span class="line"></span><br><span class="line">    headerMessage: <span class="string">&#x27;Coming Soon&#x27;</span>,</span><br><span class="line"></span><br><span class="line">    responseMessage: <span class="string">&#x27;&#x27;</span>,  <span class="comment">// 设置默认值为空字符串</span></span><br><span class="line"></span><br><span class="line">    emailAddress: <span class="string">&#x27;&#x27;</span>,  <span class="comment">// 设置默认值为空字符串</span></span><br><span class="line">    <span class="comment">//  使用正则表达式判断邮箱格式，如果正确则返回true反之返回false</span></span><br><span class="line">    isValid: Ember.computed.match(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="regexp">/^.+@.+\..+$/</span>),</span><br><span class="line">    <span class="comment">// 把计算属性isValid绑定到isDisabled上</span></span><br><span class="line">    isDisabled: Ember.computed.not(<span class="string">&#x27;isValid&#x27;</span>),  <span class="comment">//当`disabled=false`时按钮可用，所以正好需要取反</span></span><br><span class="line"></span><br><span class="line">    actions: &#123;</span><br><span class="line">        saveInvitation: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">const</span> email = <span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>);</span><br><span class="line">            <span class="comment">//  创建一个模型对象</span></span><br><span class="line">            <span class="keyword">const</span> newInvitaction = <span class="built_in">this</span>.store.createRecord(<span class="string">&#x27;invitation&#x27;</span>, &#123; <span class="attr">email</span>: email &#125;);</span><br><span class="line">            newInvitaction.save();  <span class="comment">//保存模型对象到store中</span></span><br><span class="line">            <span class="built_in">this</span>.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Thank you! We&#x27;ve just saved your email address: <span class="subst">$&#123;<span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span>`</span>);</span><br><span class="line">            <span class="comment">//  情况输入框内容</span></span><br><span class="line">            <span class="built_in">this</span>.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>等待项目重新启动，在界面输入正确的邮箱，点击按钮，可以在浏览器控制台看到如下错误信息：</p>
<p><img src="/image/blog-image/141.png" alt="错误信息"></p>
<p>为何会出现这些错误呢？？其实很简单，我们并没有对应的后台服务区处理数据，目前仅仅是提交了数据而已，那么怎么处理呢？我们引入一个非常好用的数据库——<a target="_blank" rel="noopener" href="http://firebase.com/">firebase</a>。firebase为Ember提交了非常丰富的API，我们不需要再自己写后台的处理程序，可以直接调用firebase提供的API即可完成数据的CRUD操作。更多有关firebase的使用教程请看<a target="_blank" rel="noopener" href="https://www.firebase.com/docs/web/libraries/ember/guide.html">EmberFire Guide</a>，在这个参考文档上详细介绍了firebase如何与Ember整合，Ember如何调用firebase提供的API。</p>
<p>下面简单介绍如何把firebase整合到本项目中。</p>
<ol>
<li>安装 <code>ember install emberfire</code>，会自动创建文件<code>app/adapters/application.js</code></li>
<li>在firebase官网注册一个用户，并创建一个APP（如下图1位置创建APP）然后会得到一个管理连接（比如：luminous-xxx-xxx.firebaseIO.com）</li>
<li>修改项目中<code>config/environment.js</code>，在文件中增加firebase的配置。</li>
</ol>
<p><img src="/image/blog-image/142.png" alt="图1"><br>图1</p>
<p><code>config/environment.js</code>配置代码如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> ENV = &#123;</span><br><span class="line">    modulePrefix: <span class="string">&#x27;library-app&#x27;</span>,</span><br><span class="line">    environment: environment,</span><br><span class="line">    contentSecurityPolicy: &#123; <span class="string">&#x27;connect-src&#x27;</span>: <span class="string">&quot;&#x27;self&#x27; https://auth.firebase.com wss://*.firebaseio.com&quot;</span> &#125;,</span><br><span class="line">    firebase: <span class="string">&#x27;https://YOUR-FIREBASE-NAME.firebaseio.com/&#x27;</span>,  <span class="comment">//改成自己在firebase上APP的地址</span></span><br><span class="line">    baseURL: <span class="string">&#x27;/&#x27;</span>,</span><br><span class="line">    locationType: <span class="string">&#x27;auto&#x27;</span>,</span><br><span class="line">    EmberENV: &#123;</span><br><span class="line">      FEATURES: &#123;</span><br><span class="line">        <span class="comment">// Here you can enable experimental features on an ember canary build</span></span><br><span class="line">        <span class="comment">// e.g. &#x27;with-controller&#x27;: true</span></span><br><span class="line">      &#125;</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    APP: &#123;</span><br><span class="line">      <span class="comment">// Here you can pass flags/options to your application instance</span></span><br><span class="line">      <span class="comment">// when it is created</span></span><br><span class="line">    &#125;</span><br><span class="line">  &#125;;</span><br><span class="line"><span class="comment">//  ……其他代码省略</span></span><br></pre></td></tr></table></figure>
<p>注意上述代码中的第5行，<code>firebase</code>属性的值是自己在firebase申请的APP的URL。<strong>一定记得要修改！！！</strong></p>
<p>修改完成之后手动重启项目，记得是手动关闭终端运行的项目（<code>ctrl+c</code>关闭），然后再使用命令<code>ember s</code>启动项目。否则新安装的<code>emberfire</code>无法起作用。</p>
<p>等待项目启动完成，如果启动过程中没有出现错误，说明<code>emberfire</code>安装成功！</p>
<p>然后激动的时刻到了，在首页输入正确的邮箱，点击按钮，可以看到浏览器控制来不会报错了！并且在firebase官网的APP中看到刚刚新增的邮箱！！</p>
<p><strong>注意</strong>：点击按钮提交后可能看到界面没有任何反应，先别急，由于firebase是外国的东西，在天朝访问都是比较慢，你懂的。提交后到响应回来可能比较慢。</p>
<p>从浏览器控制台打印的日志可以看出向firebase发送请求，截图如下：</p>
<p><img src="/image/blog-image/143.png" alt="向firebase发送请求截图"></p>
<p>并且在界面上提示了保存成功的信息！</p>
<p>最后在firebase官网上可以查看到刚刚提交数据。</p>
<p><img src="/image/blog-image/145.png" alt="提交到firebase的数据"></p>
<p>可以感受到firebase的强大了吧！我们几乎没有做任何处理数据就直接保存到firebase了，并且会自动根据模型创建数据，不过需要注意的是我们在模型定义中并不需要定义<code>id</code>属性，firebase会自动生成一个唯一的<code>id</code>属性值，截图中的<code>-KEr3XwUQjgLjb5yx0dp</code>就是<code>id</code>属性值。</p>
<p>到此，数据的保存工作完成了，借助firebase大大简化了自己需要处理的东西，不需要自己创建数据库、数据表、以及保存数据sql等等！不知道你是否看明白了，如果有疑问请及时给我留言，我会尽力为你解答！</p>
<h2 id="promise和this"><a href="#promise和this" class="headerlink" title="promise和this"></a>promise和this</h2><p>promise（承诺）在JavaScript中是一个异步特性。这个特性还在完善之中，更多有关promise的介绍请看<a target="_blank" rel="noopener" href="http://liubin.github.io/promises-book/">promises-book</a>或者<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Mozilla MDN Promise</a>。</p>
<p>在前面保存数据的代码中<code>save()</code>方法返回值就是一个<code>promise</code>，我们可以根据<code>save()</code>方法的返回值做不同的处理，比如保存失败时候的处理。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">saveInvitation: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> email = <span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>);</span><br><span class="line">    <span class="comment">//  创建一个模型对象</span></span><br><span class="line">    <span class="keyword">const</span> newInvitaction = <span class="built_in">this</span>.store.createRecord(<span class="string">&#x27;invitation&#x27;</span>, &#123; <span class="attr">email</span>: email &#125;);</span><br><span class="line">     <span class="comment">//保存模型对象到store中</span></span><br><span class="line">    newInvitaction.save().then(<span class="function"><span class="keyword">function</span>(<span class="params">msg</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;保存成功。&#x27;</span>);</span><br><span class="line">    &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;保存失败！&#x27;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="built_in">this</span>.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Thank you! We&#x27;ve just saved your email address: <span class="subst">$&#123;<span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span>`</span>);</span><br><span class="line">    <span class="comment">//  情况输入框内容</span></span><br><span class="line">    <span class="built_in">this</span>.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果你看过有关promise的介绍那么理解上述代码应该是很简单的，在方法<code>then()</code>中第一个函数（参数）会在<code>save()</code>执行成功的时候执行，第二个函数（参数）会在<code>save()</code>执行失败的时候执行。明白这个之后我们再修改控制器<code>index.js</code>的代码。我们把提示信息放在<code>save()</code>执行成功的时候执行方法中。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">saveInvitation: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> email = <span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>);</span><br><span class="line">    <span class="comment">//  创建一个模型对象</span></span><br><span class="line">    <span class="keyword">const</span> newInvitaction = <span class="built_in">this</span>.store.createRecord(<span class="string">&#x27;invitation&#x27;</span>, &#123; <span class="attr">email</span>: email &#125;);</span><br><span class="line">     <span class="comment">//保存模型对象到store中</span></span><br><span class="line">    newInvitaction.save().then(<span class="function"><span class="keyword">function</span>(<span class="params">msg</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Thank you! We&#x27;ve just saved your email address: <span class="subst">$&#123;<span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span>`</span>);</span><br><span class="line">        <span class="comment">//  情况输入框内容</span></span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>) </span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Saved: <span class="subst">$&#123;<span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span> failed！`</span>);</span><br><span class="line">        <span class="comment">//  情况输入框内容</span></span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>等待项目自动重启完成，在界面输入正确邮箱，提交数据，此时并没有出现任何反应，并且会在浏览器控制台看到如下错误，</p>
<p><img src="/image/blog-image/146.png" alt="错误信息"></p>
<p>这又是什么原因呢？其实原因很简单，因为<code>this</code>作用域问题，由于是在<code>then()</code>内部使用了<code>this</code>导致此时的<code>this</code>指向的并不是控制器类了，只有在Ember的上下文中才能使用<code>set()</code>方法！我们用一个临时变量解决这个问题，代码修改为如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/controller/index.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Ember <span class="keyword">from</span> <span class="string">&#x27;ember&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Ember.Controller.extend(&#123;</span><br><span class="line"></span><br><span class="line">    headerMessage: <span class="string">&#x27;Coming Soon&#x27;</span>,</span><br><span class="line"></span><br><span class="line">    responseMessage: <span class="string">&#x27;&#x27;</span>,  <span class="comment">// 设置默认值为空字符串</span></span><br><span class="line"></span><br><span class="line">    emailAddress: <span class="string">&#x27;&#x27;</span>,  <span class="comment">// 设置默认值为空字符串</span></span><br><span class="line">    <span class="comment">//  使用正则表达式判断邮箱格式，如果正确则返回true反之返回false</span></span><br><span class="line">    isValid: Ember.computed.match(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="regexp">/^.+@.+\..+$/</span>),</span><br><span class="line">    <span class="comment">// 把计算属性isValid绑定到isDisabled上</span></span><br><span class="line">    isDisabled: Ember.computed.not(<span class="string">&#x27;isValid&#x27;</span>),  <span class="comment">//当`disabled=false`时按钮可用，所以正好需要取反</span></span><br><span class="line"></span><br><span class="line">    actions: &#123;</span><br><span class="line">        saveInvitation: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">const</span> email = <span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>);</span><br><span class="line">            <span class="comment">//  创建一个模型对象</span></span><br><span class="line">            <span class="keyword">const</span> newInvitaction = <span class="built_in">this</span>.store.createRecord(<span class="string">&#x27;invitation&#x27;</span>, &#123; <span class="attr">email</span>: email &#125;);</span><br><span class="line">            <span class="keyword">var</span> _this = <span class="built_in">this</span>;</span><br><span class="line">             <span class="comment">//保存模型对象到store中</span></span><br><span class="line">            newInvitaction.save().then(<span class="function"><span class="keyword">function</span>(<span class="params">msg</span>) </span>&#123;</span><br><span class="line">                _this.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Thank you! We&#x27;ve just saved your email address: <span class="subst">$&#123;_this.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span>`</span>);</span><br><span class="line">                <span class="comment">//  情况输入框内容</span></span><br><span class="line">                _this.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">            &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">reason</span>) </span>&#123;</span><br><span class="line">                _this.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Saved: <span class="subst">$&#123;_this.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span> failed！`</span>);</span><br><span class="line">                <span class="comment">//  情况输入框内容</span></span><br><span class="line">                _this.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>等待项目自动重启完成，在页面输入正确的邮箱并提交，可以看到此时效果与之前是一样的，然后去firebase查看结果，也是可以看到新增的数据。</p>
<p>虽然是用临时变量方式可以解决由于<code>this</code>作用域问题，但是还有更加优美的解决办法，如今几乎所有新版的浏览器引擎已经支持ES2015，可以使用ES2015的<code>=&gt;</code>操作符解决<code>this</code>作用域问题，请看下面的处理代码：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">saveInvitation: <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">const</span> email = <span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>);</span><br><span class="line">    <span class="comment">//  创建一个模型对象</span></span><br><span class="line">    <span class="keyword">const</span> newInvitaction = <span class="built_in">this</span>.store.createRecord(<span class="string">&#x27;invitation&#x27;</span>, &#123; <span class="attr">email</span>: email &#125;);</span><br><span class="line"></span><br><span class="line">     <span class="comment">//保存模型对象到store中</span></span><br><span class="line">    newInvitaction.save().then(<span class="function">(<span class="params">response</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">&#x27;response = &#x27;</span> + response);</span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Thank you! We&#x27;ve just saved your email address: <span class="subst">$&#123;response.get(<span class="string">&#x27;id&#x27;</span>)&#125;</span>`</span>);</span><br><span class="line">        <span class="comment">//  情况输入框内容</span></span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    &#125;, <span class="function">(<span class="params">reason</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;responseMessage&#x27;</span>, <span class="string">`Saved: <span class="subst">$&#123;<span class="built_in">this</span>.get(<span class="string">&#x27;emailAddress&#x27;</span>)&#125;</span> failed！`</span>);</span><br><span class="line">        <span class="comment">//  情况输入框内容</span></span><br><span class="line">        <span class="built_in">this</span>.set(<span class="string">&#x27;emailAddress&#x27;</span>, <span class="string">&#x27;&#x27;</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>使用ES2015的特性之后不仅解决了<code>this</code>作用域问题，而且连关键字<code>function</code>都不需要了，使用<code>=&gt;</code>操作会自动把外层<code>this</code>所指的对象传递到函数内部，并且修改了保存成功时的提示信息，使用<code>$&#123;response.get(&#39;id&#39;)&#125;</code>从firebase响应的数据中获取到保存成功后返回的<code>id</code>值，返回的<code>response</code>就是一个模型<code>invitation</code>的对象，可以使用<code>get()</code>方法获取对象值。<br>再次测试，如果项目代码没有误那么你可以得到如下截图的提示信息（<code>id</code>值跟你的是不一样的），</p>
<p><img src="/image/blog-image/147.png" alt="使用ES2015后保存截图"></p>
<p>如果你对<code>this</code>不是很懂，请看认真看下面文章的解释：</p>
<ol>
<li><a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">Mozilla MDN this</a></li>
<li><a target="_blank" rel="noopener" href="http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html">Javascript的this用法</a></li>
</ol>
<h2 id="创建管理页面"><a href="#创建管理页面" class="headerlink" title="创建管理页面"></a>创建管理页面</h2><p>前面已经介绍了如何整合firebase到项目中，并且已经成功保存增加的数据。可以在firebase上看到所有数据，我们创建一个后台页面去管理这些数据。</p>
<p>下面创建一个子路由和路由对应的模板页面，仍然是使用Ember CLI命令创建，命令如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ember g route admin&#x2F;invitaction</span><br></pre></td></tr></table></figure>
<p>命令执行完毕后会得到一个路由文件（<code>app/routes/admin/invitaction.js</code>）和一个模板文件（<code>app/templates/admin/invitaction.hbs</code>）,命令会自动创建文件夹<code>admin</code>，子路由和子模板会放在子子目录下。<br>然后在首页增加菜单链接，修改<code>navbar.hbs</code>模板。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/templates/navbar.hbs --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">nav</span> <span class="attr">class</span>=<span class="string">&quot;navbar navbar-inverse&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;container-fluid&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;navbar-header&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;button&quot;</span> <span class="attr">class</span>=<span class="string">&quot;navbar-toggle collapsed&quot;</span> <span class="attr">data-toggle</span>=<span class="string">&quot;collapse&quot;</span> <span class="attr">data-target</span>=<span class="string">&quot;#main-navbar&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;sr-only&quot;</span>&gt;</span>Toggle navigation<span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;icon-bar&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;icon-bar&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;icon-bar&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">      &#123;&#123;#link-to &#x27;index&#x27; class=&quot;navbar-brand&quot;&#125;&#125;Library App&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;collapse navbar-collapse&quot;</span> <span class="attr">id</span>=<span class="string">&quot;main-navbar&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">&quot;nav navbar-nav&quot;</span>&gt;</span></span><br><span class="line">            &#123;&#123;#link-to &#x27;index&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>&gt;</span>Home<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">            &#123;&#123;#link-to &#x27;about&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>&gt;</span>About<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">            &#123;&#123;#link-to &#x27;contact&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>&gt;</span>Contact<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">      <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">      <span class="comment">&lt;!-- 后台管理页面链接 --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">&quot;nav navbar-nav navbar-right&quot;</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">li</span> <span class="attr">class</span>=<span class="string">&quot;dropdown&quot;</span>&gt;</span></span><br><span class="line">              <span class="tag">&lt;<span class="name">a</span> <span class="attr">class</span>=<span class="string">&quot;dropdown-toggle&quot;</span> <span class="attr">data-toggle</span>=<span class="string">&quot;dropdown&quot;</span> <span class="attr">role</span>=<span class="string">&quot;button&quot;</span> <span class="attr">aria-haspopup</span>=<span class="string">&quot;true&quot;</span> <span class="attr">aria-expanded</span>=<span class="string">&quot;false&quot;</span>&gt;</span>Admin<span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">&quot;caret&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">span</span>&gt;</span><span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">              <span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">&quot;dropdown-menu&quot;</span>&gt;</span></span><br><span class="line">                  &#123;&#123;#link-to &#x27;admin.invitation&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>Invitations<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">              <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;/<span class="name">li</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="comment">&lt;!-- /.navbar-collapse --&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span><span class="comment">&lt;!-- /.container-fluid --&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">nav</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>代码<code>{{#link-to 'admin.invitation' tagName="li"}}<a href="">Invitations</a>{{/link-to}}</code>中<code>admin.invitation</code>是一个嵌套路由或者说是子路由。更多有关路由嵌套问题请看<a target="_blank" rel="noopener" href="http://blog.ddlisting.com/2016/03/22/ember-js-ru-men-zhi-nan-zhi-shi-san-link-to/">Ember.js 入门指南之十三{{link-to}} 助手</a>。</p>
<p>在模板中使用表格遍历显示所有的邮箱数据。修改模板<code>invitaction.hbs</code>。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/templates/admin/invitations.hbs --&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Invitations<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">table</span> <span class="attr">class</span>=<span class="string">&quot;table table-bordered table-striped&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">thead</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">tr</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">th</span>&gt;</span>ID<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">th</span>&gt;</span>E-mail<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">thead</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">tbody</span>&gt;</span></span><br><span class="line">    &#123;&#123;#each model as |invitation|&#125;&#125;</span><br><span class="line">        <span class="tag">&lt;<span class="name">tr</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">th</span>&gt;</span>&#123;&#123;invitation.id&#125;&#125;<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123;invitation.email&#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br><span class="line">    &#123;&#123;/each&#125;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;/<span class="name">tbody</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">table</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>上述代码中<code>{{#each}}{{/each}}</code>是Ember提供的遍历表达式，此表达式用于遍历数组数据。本例子中用户遍历从路由的<code>model</code>回调中返回的数据。更多有关此表达式的介绍请看<a target="_blank" rel="noopener" href="http://blog.ddlisting.com/2016/03/18/ember-js-ru-men-zhi-nan-zhi-shi-handlebarsbian-li-biao-qian/">Ember.js 入门指南之十handlebars遍历标签</a>。<br>修改路由<code>app/routes/admin/invitations.js</code>在<code>model</code>回调中获取服务器（firebase）上的数据。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/routes/admin/invitations.js</span></span><br><span class="line"><span class="keyword">import</span> Ember <span class="keyword">from</span> <span class="string">&#x27;ember&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Ember.Route.extend(&#123;</span><br><span class="line"></span><br><span class="line">  model() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.store.findAll(<span class="string">&#x27;invitation&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>等待项目重启完成，可以在项目首页导航栏的右侧看到可以点击下拉的Admin菜单项，点击菜单看到子菜单项“Invitation”，点击“Invitation”进入到<a target="_blank" rel="noopener" href="http://localhost:4200/admin/invitation">http://localhost:4200/admin/invitation</a>。<br>在界面上可以看到之前新增的所有邮箱信息和firebase自动生成的<code>ID</code>属性值（由于firebase是老外的东西获取数据会比较慢，数据显示自然也会比较慢，稍等一会就在界面上看到了！）。如果你项目代码没问题也可以看到如下的截图。</p>
<p><img src="/image/blog-image/149.png" alt="Invitation数据截图"></p>
<h2 id="数据的CRUD操作"><a href="#数据的CRUD操作" class="headerlink" title="数据的CRUD操作"></a>数据的CRUD操作</h2><p>到这一步我们已经可以完整的从服务器获取数据，并以列表形式显示在界面上。本教程的目标是创建一个简单的图书管理系统，前面的文章已经完成了类似于用户注册的功能，接下来我们创建一个<code>library</code>模型，用于保存书库信息。<br>同样是使用Ember CLI命令创建模型。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ember g model library name:string address:string phone:string</span><br></pre></td></tr></table></figure>
<p>上述命令创建了一个包含三个属性的模型，这三个属性都是<code>string</code>类型的数据。创建完模型之后再继续创建三个模板，分别用户显示library列表新建library数据。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ember g template libraries</span><br><span class="line">ember g template libraries&#x2F;index</span><br><span class="line">ember g template libraries&#x2F;new</span><br></pre></td></tr></table></figure>
<p>模板创建完毕之后手动在<code>router.js</code>中增加路由配置，这次我们不采用Ember CLI命令创建了！！！</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/router.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> Ember <span class="keyword">from</span> <span class="string">&#x27;ember&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> config <span class="keyword">from</span> <span class="string">&#x27;./config/environment&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Router = Ember.Router.extend(&#123;</span><br><span class="line">  location: config.locationType</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">Router.map(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="built_in">this</span>.route(<span class="string">&#x27;about&#x27;</span>);</span><br><span class="line">  <span class="built_in">this</span>.route(<span class="string">&#x27;contact&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">this</span>.route(<span class="string">&#x27;admin&#x27;</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.route(<span class="string">&#x27;invitation&#x27;</span>);</span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="built_in">this</span>.route(<span class="string">&#x27;libraries&#x27;</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">this</span>.route(<span class="string">&#x27;new&#x27;</span>);</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Router;</span><br></pre></td></tr></table></figure>
<p>再更新首页模板<code>navbar.hbs</code>，增加一个菜单项“libraries”，其他代码不变。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">&quot;nav navbar-nav&quot;</span>&gt;</span></span><br><span class="line">    &#123;&#123;#link-to &#x27;index&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>Home<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">    &#123;&#123;#link-to &#x27;libraries&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>Libraries<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">    &#123;&#123;#link-to &#x27;about&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>About<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">    &#123;&#123;#link-to &#x27;contact&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>Contact<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>修改模板<code>libraries.hbs</code>，增加菜单链接。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/templates/libraries.hbs --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Libraries<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;well&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">ul</span> <span class="attr">class</span>=<span class="string">&quot;nav nav-pills&quot;</span>&gt;</span></span><br><span class="line">      &#123;&#123;#link-to &#x27;libraries.index&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>List all<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">      &#123;&#123;#link-to &#x27;libraries.new&#x27; tagName=&quot;li&quot;&#125;&#125;<span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>&gt;</span>Add new<span class="tag">&lt;/<span class="name">a</span>&gt;</span>&#123;&#123;/link-to&#125;&#125;</span><br><span class="line">    <span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="comment">&lt;!-- 子模板new.hbs、index.hbs会渲染到outlet上 --&gt;</span></span><br><span class="line">&#123;&#123;outlet&#125;&#125;</span><br></pre></td></tr></table></figure>
<p>等待项目重启完成，进入<a target="_blank" rel="noopener" href="http://localhost:4200/libraries">http://localhost:4200/libraries</a>。可以看到如下图的界面</p>
<p><img src="/image/blog-image/150.png" alt="libraries界面"></p>
<p>此时点击界面的上的“List all”和“Add new”除了看到URL变化之外还没任何效果，因为我们的子模板<code>libraries/index.hbs</code>、<code>libraries/new.hbs</code>还没有任何内容，下面在这两个模板中增加一些代码。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/templates/libraries/index.hbs --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h2</span>&gt;</span>List<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line"></span><br><span class="line">&#123;&#123;#each model as |library|&#125;&#125;</span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;panel panel-default&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;panel-heading&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">h3</span> <span class="attr">class</span>=<span class="string">&quot;panel-title&quot;</span>&gt;</span>&#123;&#123;library.name&#125;&#125;<span class="tag">&lt;/<span class="name">h3</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;panel-body&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">p</span>&gt;</span>Address: &#123;&#123;library.address&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">p</span>&gt;</span>Phone: &#123;&#123;library.phone&#125;&#125;<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">&#123;&#123;/each&#125;&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- app/templates/libraries/new.hbs --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h2</span>&gt;</span>Add a new local Library<span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;form-horizontal&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;form-group&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-2 control-label&quot;</span>&gt;</span>Name<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-10&quot;</span>&gt;</span></span><br><span class="line">            &#123;&#123;input type=&quot;text&quot; value=model.name class=&quot;form-control&quot; placeholder=&quot;The name of the Library&quot;&#125;&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;form-group&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-2 control-label&quot;</span>&gt;</span>Address<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-10&quot;</span>&gt;</span></span><br><span class="line">            &#123;&#123;input type=&quot;text&quot; value=model.address class=&quot;form-control&quot; placeholder=&quot;The address of the Library&quot;&#125;&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;form-group&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">label</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-2 control-label&quot;</span>&gt;</span>Phone<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-10&quot;</span>&gt;</span></span><br><span class="line">            &#123;&#123;input type=&quot;text&quot; value=model.phone class=&quot;form-control&quot; placeholder=&quot;The phone number of the Library&quot;&#125;&#125;</span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;form-group&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;col-sm-offset-2 col-sm-10&quot;</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">button</span> <span class="attr">type</span>=<span class="string">&quot;submit&quot;</span> <span class="attr">class</span>=<span class="string">&quot;btn btn-default&quot;</span> &#123;&#123;<span class="attr">action</span> &#x27;<span class="attr">saveLibrary</span>&#x27; <span class="attr">model</span>&#125;&#125;&gt;</span>Add to library list<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>
<p>模板<code>new.hbs</code>是一个表单，用于新增数据，通过点击按钮“Add to library list”提交表单数据，表单数据由路由<code>libraries/new.js</code>中的<code>saveLibrary</code>方法处理，此时此方法还没定义，在接下来的代码中会定义。<br>在<code>{{action}}</code>表达式中传递了一个参数<code>model</code>到处理的后台，表单中的其他属性会以<code>model</code>的属性方式传递到后台，之所以可以这样做是因为在模板对应的路由中返回了一个空的<code>library</code>对象，在接下来的路由<code>libraries/new.js</code>将看到。<br>等待项目重启完，在点击“List all”和“Add new”可以看到这两个子模板的内容渲染到父模板<code>libraries.hbs</code>的<code>{{outlet}}</code>上。不过由于并没有在路由中获取模型<code>library</code>的数据所以“List all”页面还没有任何数据，“Add new”页面是第一个新增数据吧表单。<br>下面在路由<code>libraries/index.js</code>中获取library的数据，并在<code>model</code>回调中返回到模板中遍历显示。<br>使用Ember CLI命令创建路由，创建过程会询问是否覆盖已经存在的模板文件，输入<code>n</code>选择否。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ember g route libraries&#x2F;index</span><br><span class="line">ember g route libraries&#x2F;new</span><br></pre></td></tr></table></figure>
<p>路由创建完成之后分别在这两个路由中增加获取数据的代码。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/routes/libraries/index.js</span></span><br><span class="line"><span class="keyword">import</span> Ember <span class="keyword">from</span> <span class="string">&#x27;ember&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Ember.Route.extend(&#123;</span><br><span class="line"></span><br><span class="line">  model() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.store.findAll(<span class="string">&#x27;library&#x27;</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// app/routes/libraries/new.js</span></span><br><span class="line"><span class="keyword">import</span> Ember <span class="keyword">from</span> <span class="string">&#x27;ember&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> Ember.Route.extend(&#123;</span><br><span class="line"></span><br><span class="line">  model() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">this</span>.store.createRecord(<span class="string">&#x27;library&#x27;</span>);</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  actions: &#123;</span><br><span class="line">    <span class="comment">//  处理模板上输入的数据</span></span><br><span class="line">    saveLibrary(newLibrary) &#123;</span><br><span class="line">      newLibrary.save().then(<span class="function">() =&gt;</span> <span class="built_in">this</span>.transitionTo(<span class="string">&#x27;libraries&#x27;</span>));</span><br><span class="line">    &#125;,</span><br><span class="line"></span><br><span class="line">    willTransition() &#123;</span><br><span class="line">      <span class="comment">// rollbackAttributes() removes the record from the store</span></span><br><span class="line">      <span class="comment">// if the model &#x27;isNew&#x27;</span></span><br><span class="line">      <span class="built_in">this</span>.controller.get(<span class="string">&#x27;model&#x27;</span>).rollbackAttributes();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
<p>在此路由的<code>model</code>回调中我们创建了一个空的<code>library</code>对象，并返回到模板页面。这也是为什么能在模板中传递参数<code>model</code>的原因。<br>代码中方法<code>willTransition()</code>是Ember提供的内置方法，此方法的作用是当用户离开当前URL时会清空未保存到服务器的<code>library</code>数据，如果不重置<code>model</code>Ember会在路由切换的时候自动保存数据到服务器上。<br>保存数据的方法<code>saveLibrary()</code>写的比较简洁，它的作用是：先调用<code>save()</code>方法保存数据，如果保存成功在调用方法<code>transitionTo()</code>跳转到路由<code>libraries</code>下（library首页），有关<code>=&gt;</code>语法的介绍请看<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Syntax">Mozill MDN Arrow_functions</a>。<br>在上述代码中多次是用了<code>this.controller</code>，但是在路由中并没有这个属性<code>controller</code>而且也没有控制器文件<code>app/controllers/libraries/new.js</code>，运行项目并不会报错！这是为什么呢？这是因为Ember会自动生成一个虚拟的控制器并在保存在内存中，根据Ember的命名规则会自动生成一个与路由同名的控制器，<br>为了验证这个说法，打开浏览器的控制台，在打开标签“Ember”然后点击左侧的“/# Routes”，找到路由<code>libraries</code>这一块，可以看到如下截图的信息。</p>
<p><img src="/image/blog-image/151.png" alt="自动生成控制器"></p>
<p>可以看到每个路由都对应着一个同名的控制器。</p>
<p>等待项目重启完毕，开始验证代码是否实现了所设想的要求。<br>首先在新增页面输入如下截图信息，然后点击按钮保存数据。</p>
<p><img src="/image/blog-image/152.png" alt="新增library数据"></p>
<p>稍等片刻，等待数据保存完毕，可以看到界面顺利跳转到了<a target="_blank" rel="noopener" href="http://localhost:4200/libraries">http://localhost:4200/libraries</a>下，如下图所示，并且看到了刚刚新增的数据，为了验证数据是否真的保存到服务器中，我们进入到firebase的APP中查看，可以看到数据以及保存到里library下。</p>
<p><img src="/image/blog-image/153.png" alt="libraries页面"></p>
<p>library数据列表页面截图<br></p>
<p><img src="/image/blog-image/154.png" alt="firebase页面"></p>
<p>firebase上保存的library数据截图</p>
<p>此时，如果你注释了方法<code>willTransition()</code>结果会是怎么样的呢！！如果没有这个方法去重置<code>model</code>，当你每次在“Add new”页面输入输入并且没有点击“Add to library list”保存数，然后切换到其他路由下（比如点击“List all”切换到路由<code>libraries</code>下）会自动保存一条数据到服务器。</p>
<ul>
<li>在“Add new”页面输入如下截图数据</li>
</ul>
<p><img src="/image/blog-image/156.png" alt="未提交保存的数据"></p>
<ul>
<li>点击按钮“List all”切换到路由<code>libraries</code>下，可以看到在“Add new”页面添加的数据，如下图所示，但是如果你手动刷新页面后可以发现这条数据不见了，并且在firebase上也没有这条数据，可见这条数据仅仅是保存到Ember的<a target="_blank" rel="noopener" href="https://guides.emberjs.com/v2.4.0/models/">store</a>中，并没有真正保存到服务器上。这样的体验是非常糟糕的！！</li>
</ul>
<p><img src="/image/blog-image/157.png" alt="显示为提交保存的数据"></p>
<p>其中,实现重置<code>model</code>的方式还有另外一种更加合适的方法，代码如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">willTransition() &#123;</span><br><span class="line">  <span class="comment">// rollbackAttributes() removes the record from the store</span></span><br><span class="line">  <span class="comment">// if the model &#x27;isNew&#x27;</span></span><br><span class="line"><span class="comment">//   this.controller.get(&#x27;model&#x27;).rollbackAttributes();</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">let</span> model = <span class="built_in">this</span>.controller.get(<span class="string">&#x27;model&#x27;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (model.get(<span class="string">&#x27;isNew&#x27;</span>)) &#123;</span><br><span class="line">    model.destroyRecord();</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>本篇的内容到此全部介绍完毕！本篇我们实现了数据的保存、显示，特别是library数据的保存。数据的保存、显示都需要与模型关联，模型在Ember是一个非常重要的内容！希望读者好好掌握模型。</p>
<h2 id="家庭作业"><a href="#家庭作业" class="headerlink" title="家庭作业"></a>家庭作业</h2><p>下面两个作业完成其中之一即可。本篇选择第一个，第二个请读者自行完成！动手才是真理。</p>
<h4 id="增强contact的功能"><a href="#增强contact的功能" class="headerlink" title="增强contact的功能"></a>增强contact的功能</h4><ol>
<li>创建一个包括两个属性<code>email</code>和<code>message</code>的模型<code>contact</code>，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/models/contact.js">参考代码</a></li>
<li>修改路由<code>app/routes/contact.js</code>返回一个空对象到模板上，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/routes/contact.js">参考代码</a></li>
<li>修改控制器<code>contact.js</code>，保存数据到firebase，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/controllers/contact.js">参考代码</a></li>
<li>把放在控制器中的校验代码移动到模型<code>app/models/contact.js</code>中，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/models/contact.js">参考代码</a></li>
<li>创建一个管理contact的后台页面<a target="_blank" rel="noopener" href="http://localhost:4200/admin/contacts">http://localhost:4200/admin/contacts</a>，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/templates/admin/contact.hbs">参考代码</a></li>
<li>在项目首页的导航菜单上增加一个菜单项“Contacts”，点击菜单进入<a target="_blank" rel="noopener" href="http://localhost:4200/admin/contact">http://localhost:4200/admin/contact</a>，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/templates/navbar.hbs">参考代码</a></li>
<li>使用表格展示所有的contact数据，<a target="_blank" rel="noopener" href="https://github.com/ubuntuvim/library-app/blob/master/app/templates/admin/contact.hbs">参考代码</a></li>
</ol>
<h4 id="使用路由和模型重构有关contact的代码"><a href="#使用路由和模型重构有关contact的代码" class="headerlink" title="使用路由和模型重构有关contact的代码"></a>使用路由和模型重构有关contact的代码</h4><ol>
<li>把有关contact的检验放到模型类中</li>
<li>把控制器<code>contact.js</code>中保存数据的代码移动到同名的路由中</li>
<li>测试通过后删除控制器<code>contact.js</code></li>
</ol>
<p><strong>作业演示结果</strong></p>
<p><img src="/image/blog-image/159.png" alt="新增contact信息"></p>
<p>当输入的邮箱格式正确，并且message长度大于6时按钮“send”才可用。</p>
<p><img src="/image/blog-image/160.png" alt="新增contact信息成功"></p>
<p>保存成功后情况输入框，并且显示提示信息。当切换路由后再进入到<a target="_blank" rel="noopener" href="http://localhost:4200/contact">http://localhost:4200/contact</a>会清空保存成功的提示信息。</p>
<p><img src="/image/blog-image/161.png" alt="后台Contact列表"></p>
<p>后台页面成功显示了新增的数据，即使手动刷新页面数据也不会丢失，可见数据已经保存到firebase，在此不再贴firebase上的数据截图了！</p>
<br>
为了照顾懒人我把完整的代码放在[GitHub](https://github.com/ubuntuvim/library-app)上，如有需要请参考参考。博文经过多次修改，博文上的代码与github代码可能有出入，不过影响不大！如果你觉得博文对你有点用，请在github项目上给我点个`star`吧。您的肯定对我来说是最大的动力！！

    </div>

      
    <div class="post-nav">
      <div class="post-nav-item post-nav-next">
        
          <span>〈 </span>
          <a href="/2020/01/18/Ember-Demo/%E5%BC%95%E5%85%A5%E8%AE%A1%E7%AE%97%E5%B1%9E%E6%80%A7%E3%80%81action%E3%80%81%E5%8A%A8%E6%80%81%E5%86%85%E5%AE%B9/" rel="next" title="引入计算属性、action、动态内容">
          引入计算属性、action、动态内容
          </a>
        
      </div>
  
      <div class="post-nav-item post-nav-prev">
          
          <a href="/2020/01/18/Linux/ubuntu%E5%AE%89%E8%A3%85%E4%B8%BB%E9%A2%98/" rel="prev" title="ubuntu安装主题">
            ubuntu安装主题
          </a>
          <span>〉</span>
        
      </div>
    </div>
  
  </article>
  <div class="toc-container">
    
  <div id="toc" class="toc-article">
    <strong class="toc-title">目录</strong>
    <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%AC%AC%E4%B8%80%E4%B8%AAEmber-js%E6%A8%A1%E5%9E%8B"><span class="toc-text">第一个Ember.js模型</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#promise%E5%92%8Cthis"><span class="toc-text">promise和this</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E7%AE%A1%E7%90%86%E9%A1%B5%E9%9D%A2"><span class="toc-text">创建管理页面</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%95%B0%E6%8D%AE%E7%9A%84CRUD%E6%93%8D%E4%BD%9C"><span class="toc-text">数据的CRUD操作</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%AE%B6%E5%BA%AD%E4%BD%9C%E4%B8%9A"><span class="toc-text">家庭作业</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%A2%9E%E5%BC%BAcontact%E7%9A%84%E5%8A%9F%E8%83%BD"><span class="toc-text">增强contact的功能</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E8%B7%AF%E7%94%B1%E5%92%8C%E6%A8%A1%E5%9E%8B%E9%87%8D%E6%9E%84%E6%9C%89%E5%85%B3contact%E7%9A%84%E4%BB%A3%E7%A0%81"><span class="toc-text">使用路由和模型重构有关contact的代码</span></a></li></ol></li></ol></li></ol>
  </div>


  </div>
</div>


<div class="copyright">
    <span>本作品采用</span>
    <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by/4.0/">知识共享署名 4.0 国际许可协议</a>
    <span>进行许可。 转载时请注明原文链接。</span>
</div>
<div class="share">

</div>
<div class="post-container">
    <article class="post">
      <div id="container"></div>
    </article>
</div>

<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">
<script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
<script>
var gitment = new Gitment({
  id: '模型，保存数据到数据库', // 可选。默认为 location.href
  owner: 'ubuntuvim',
  repo: 'ubuntuvim.github.io',
  oauth: {
    client_id: '48b3df0bf4ba1743e0a3',
    client_secret: 'c6c2cec09acb5eecc9d325d56cef187264aa1e16',
  },
})
gitment.render('container')
</script>



    </div>

    

  </div>
  <footer class="footer text-center">
    <div id="bottom-inner">
        <a class="bottom-item" href="http://xcoding.tech/">首页</a> |
        <a class="bottom-item" href="http://xcoding.tech/" target="_blank">主站</a> |
        <a class="bottom-item" href="https://github.com/ubuntuvim" target="_blank">GitHub</a> |
        <a class="bottom-item" href="https://hexo.io" target="_blank">Powered by hexo</a> |
        <a class="bottom-item" href="https://github.com/KevinOfNeu/hexo-theme-xoxo" target="_blank">Theme xoxo</a>
    </div>
</footer>


  <script src='https://unpkg.com/mermaid@7.1.2/dist/mermaid.min.js'></script>
  <script>
    if (window.mermaid) {
      mermaid.initialize({theme: 'forest'});
    }
  </script>


  
  <!-- scripts list from theme config.yml -->
  
    <script src="/js/zepto.min.js"></script>
  


<script>
  (function(window, document, undefined) {

    var timer = null;

    function returnTop() {
      cancelAnimationFrame(timer);
      timer = requestAnimationFrame(function fn() {
        var oTop = document.body.scrollTop || document.documentElement.scrollTop;
        if (oTop > 0) {
          document.body.scrollTop = document.documentElement.scrollTop = oTop - 50;
          timer = requestAnimationFrame(fn);
        } else {
          cancelAnimationFrame(timer);
        }
      });
    }

    var hearts = [];
    window.requestAnimationFrame = (function() {
      return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function(callback) {
          setTimeout(callback, 1000 / 60);
        }
    })();
    init();

    function init() {
      css(".heart{z-index:9999;width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: absolute;}.heart:after{top: -5px;}.heart:before{left: -5px;}");
      attachEvent();
      gameloop();
      addMenuEvent();
    }

    function gameloop() {
      for (var i = 0; i < hearts.length; i++) {
        if (hearts[i].alpha <= 0) {
          document.body.removeChild(hearts[i].el);
          hearts.splice(i, 1);
          continue;
        }
        hearts[i].y--;
        hearts[i].scale += 0.004;
        hearts[i].alpha -= 0.013;
        hearts[i].el.style.cssText = "left:" + hearts[i].x + "px;top:" + hearts[i].y + "px;opacity:" + hearts[i].alpha + ";transform:scale(" + hearts[i].scale + "," + hearts[i].scale + ") rotate(45deg);background:" + hearts[i].color;
      }
      requestAnimationFrame(gameloop);
    }

    /**
     * 给logo设置点击事件
     * 
     * - 回到顶部
     * - 出现爱心
     */
    function attachEvent() {
      var old = typeof window.onclick === "function" && window.onclick;
      var logo = document.getElementById("logo");
      if (logo) {
        logo.onclick = function(event) {
          returnTop();
          old && old();
          createHeart(event);
        }
      }
      
    }

    function createHeart(event) {
      var d = document.createElement("div");
      d.className = "heart";
      hearts.push({
        el: d,
        x: event.clientX - 5,
        y: event.clientY - 5,
        scale: 1,
        alpha: 1,
        color: randomColor()
      });
      document.body.appendChild(d);
    }

    function css(css) {
      var style = document.createElement("style");
      style.type = "text/css";
      try {
        style.appendChild(document.createTextNode(css));
      } catch (ex) {
        style.styleSheet.cssText = css;
      }
      document.getElementsByTagName('head')[0].appendChild(style);
    }

    function randomColor() {
      // return "rgb(" + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + "," + (~~(Math.random() * 255)) + ")";
      return "#F44336";
    }

    function addMenuEvent() {
      var menu = document.getElementById('menu-main-post');
      if (menu) {
        var toc = document.getElementById('toc');
        if (toc) {
          menu.onclick = function() {
            if (toc) {
              if (toc.style.display == 'block') {
                toc.style.display = 'none';
              } else {
                toc.style.display = 'block';
              }
            }
          };
        } else {
          menu.style.display = 'none';
        }
      }
    }

  })(window, document);
</script>

  



</body>
</html>
